﻿#region using statements

using Microsoft.Xna.Framework;

using NGen.Managers.GameObject;

using TowerDefense.Objects.Enemies;
using TowerDefense.Objects.Towers;

#endregion

namespace TowerDefense.Objects.Projectiles
{
	/// <summary>
	/// 	Base class for a tower projectile
	/// </summary>
	public abstract class GameObjectProjectileBase : GameObject2D
	{
		#region local fields

		/// <summary>
		/// 	the tower which fired this projectile
		/// </summary>
		protected GameObjectTowerBase m_firedByTower;

		/// <summary>
		/// 	the level of this projectile
		/// </summary>
		protected int m_projectileLevel;

		/// <summary>
		/// 	the enemy this projectile is targetting
		/// </summary>
		protected GameObjectEnemyBase m_targetEnemy;

		#endregion

		#region local properties

		/// <summary>
		/// 	speed of the projectile
		/// </summary>
		public float Velocity { get; protected set; }

		/// <summary>
		/// 	damage dealth by this projectile
		/// </summary>
		public int Damage { get; protected set; }

		#endregion

		#region constructors

		/// <summary>
		/// 	Construct a new projectile
		/// </summary>
		/// <param name = "p_enemy">enemy this projectile will seek</param>
		/// <param name = "p_tower">tower which fired this projectile</param>
		protected GameObjectProjectileBase(GameObjectEnemyBase p_enemy, GameObjectTowerBase p_tower) {
			m_targetEnemy = p_enemy;
			m_firedByTower = p_tower;
			m_projectileLevel = p_tower.TowerLevel;
		}

		#endregion

		#region local methods

		/// <summary>
		/// 	Callback called whenever the target enemy is destroyed
		/// 	(perhaps before this projectile can damage the enemy)
		/// </summary>
		/// <param name = "p_gameObject">the object which was destroyed</param>
		protected void _TargetEnemyDestroyedEvent(GameObject p_gameObject) {
			m_targetEnemy = null;
		}

		/// <summary>
		/// 	Abstract method which is implemented by each specific type of
		/// 	projectile to apply its own special effects upon reaching the
		/// 	target
		/// </summary>
		protected abstract void _TargetHit();

		#endregion

		#region GameObject2D implementation

		/// <summary>
		/// 	_Initialize this projectile
		/// </summary>
		protected override void _Initialize() {
			//hook up the callback to be notified when the target dies
			m_targetEnemy.OnObjectDestroyedEvent += _TargetEnemyDestroyedEvent;
			m_position = m_firedByTower.m_position;
			m_textureOffset = new Vector2(m_texture.Width / 2, m_texture.Height / 2);
			m_textureTint.A = 127; //set alpha to half value (8bit value)
		}

		/// <summary>
		/// 	Update the projectile for this frame
		/// </summary>
		/// <param name = "p_gameTime"></param>
		protected override void _Update(GameTime p_gameTime) {
			//if no target, kill this projectile
			if(m_targetEnemy == null) {
				m_targetEnemy = m_firedByTower.CurrentTarget;
				if(m_targetEnemy == null) {
					Destroy(Id);
					return;
				}
			}

			//get direction to move towards target
			var offset = Vector2.Subtract(m_targetEnemy.m_position, m_position);

			//if within range to deal damage
			if(offset.LengthSquared() < 16.0f) {
				m_targetEnemy.Health -= Damage;
				_TargetHit();
				Destroy(Id);
			}

			//normalize and move projectile towards calculated value
			offset.Normalize();
			m_position += offset * Velocity * (float) p_gameTime.ElapsedGameTime.TotalSeconds;
		}

		/// <summary>
		/// 	Cleans up this projectile
		/// </summary>
		protected override void _Cleanup() {
			//unhook the event notification if the target is not dead when
			//this object is destroyed
			if(m_targetEnemy != null) {
				m_targetEnemy.OnObjectDestroyedEvent -= _TargetEnemyDestroyedEvent;
			}
		}

		#endregion
	}
}